
#include <iostream>
#include <sstream>

#include "dispatcher.h"
#include "cm_net.h"

typedef void (Dispatcher::*dipatcher_func)(std::map<string, string> args);
struct Command {
	dipatcher_func func;
	string usage;

	bool match(list<string> line, string pre_usage, Map<string, string>& res);
	Command(dipatcher_func func, string usage) : func(func), usage(usage) {}
	Command(const Command& x) : func(x.func), usage(x.usage) {}
	Command() : func(0) {}
};

//void print_map(Map<string, string> map) {
//	std::cout << "args:\n";
//	for (auto x : map)
//		std::cout << x.first << " => " << x.second << "\n";
//	std::cout << "done\n";
//}

Map<string, Command> create_command() {
	Map<string, Command> res;
	res["add_file"] = Command(&Dispatcher::add_file, "<filename>");
	res["checkout"] = Command(&Dispatcher::checkout, "<filename>");
	res["checkin"] = Command(&Dispatcher::checkin, "<filename> <comment*>" );
	res["get"] = Command(&Dispatcher::get, "<filename> [<version>]");
	res["view_files"] = Command(&Dispatcher::view_files, "" );
	res["view_versions"] = Command(&Dispatcher::view_versions, "<filename>" );
	res["update_owner"] = Command(&Dispatcher::update_owner, "+|- <username> <filename>" );
	res["undo_checkout"] = Command(&Dispatcher::undo_checkout, "<filename>" );
	res["view_my_co"] = Command(&Dispatcher::view_my_co, "");
	res["quit"] = Command(&Dispatcher::quit, "");
	return res;
}

static Map<string, Command> commands =  create_command() ;

list<string> split(string str) {
	typedef std::istream_iterator<string> iter;
	std::istringstream iss(str);
	return list<string>(iter(iss), iter());
}

bool strip(string& str, char a, char b) {
	if (str[0] == a && str[str.size()-1] == b) {
		str = string(str.begin()+1, str.end()-1);
		return true;
	}
	return false;
}


template <typename T>
typename T::value_type pop_front(T& t) {
	typename T::value_type res = t.front();
	t.pop_front();
	return res;
}


bool Command::match(list<string> line, string post_usage, Map<string, string>& res) {
	list<string> usage = split(this->usage + " " + post_usage);
	while (usage.size()) {
		string k = pop_front(usage);
		if (strip(k, '[', ']') && line.size() == split(post_usage).size())
			continue;
		if (line.size() == 0)
			return false;
		string v = pop_front(line);
		if (!strip(k, '<', '>')) {
			if (v.size() != 1 || (v[0] != k[0] && v[0] != k[k.size()-1]) )
				return false;
		}
		if (k[k.length()-1]=='*') {
			k.erase(k.length()-1);
			while (line.size() > split(post_usage).size()) {
				v += " ";
				v += pop_front(line);
			}
		}
		res[k] = v;
	}
	return line.size() == 0;
}


void Dispatcher::run_command(string input) {
	list<string> line = split(input);
	string cmd = pop_front(line);
	if (commands.count(cmd) == 0) {
		std::cout << "Unknown command: " <<  cmd  << "\n";
		return;
	}

	Map<string, string> res;
	res["cmd"]=cmd;
	res["original command"]=input;
	try {
		if (commands[cmd].match(line, post_usage, res))
			(this->*(commands[cmd].func))(res);
		else
			std::cout <<  "Usage: " << cmd << " " << commands[cmd].usage << "\n";
	} catch (IllegalAction& ex) {
		this->handle(ex.error);
	} catch (ConnectionError& ex) {
		std::cout << "Error: " << ex.what << std::endl;
	}
}
